/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include "nril.h"
#include "rilConfig.h"
/* Public typedef ------------------------------------------------------------*/
/* Pravite variables ----------------------------------------------------------*/
extern struct Socket *GetSocketById(int id);

int ML302_StartUp(struct RilDevice *device);
int ML302_ShutDown(struct RilDevice *device);
int ML302_Init(struct RilDevice *device);
int ML302_Reset(struct RilDevice *device, bool usePin);
int ML302_Pdp(struct RilDevice *device);

int ML302_Release(struct RilDevice *device, struct Socket *socket);
int ML302_Connect(struct RilDevice *device, struct Socket *socket, const struct sockaddr *name, socklen_t namelen);
int ML302_Disconnect(struct RilDevice *device, struct Socket *socket);
int ML302_Send(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags);
int ML302_Recv(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags);
int ML302_SendTo(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen);
int ML302_RecvFrom(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags, struct sockaddr *from, socklen_t *fromlen);

int ML302_Sim(char *data);
int ML302_Signal(char *data);
int ML302_Imei(char *data);
int ML302_Imsi(char *data);
int ML302_PdpInfo(char *data);
int ML302_Cclk(char *data);
int ML302_SocketCon(char *data);
int ML302_SocketRx(char *data);
int ML302_GnssInfo(char *data);
int ML302_GnssType(char *data);

const struct NatUrc ml302Urcs[] =  {
        {"+CPIN",               ML302_Sim },
        {"+CSQ",                ML302_Signal },
        {"+IMEI:",            	ML302_Imei },
        {"+VERCTRL",            ML302_PdpInfo },
        {"+CCLK",               ML302_Cclk },
        {"CLOSED",              ML302_SocketCon},
        {"+MIPURC",             ML302_SocketRx},
        {"$GPGGA",              ML302_GnssInfo},
        {"$GNGGA",              ML302_GnssInfo},
        {"+MGNSSTYPE",          ML302_GnssType},
        NULL
};
	
const struct Nat ml302Nat = {.urcs = ml302Urcs};

const struct RilOperations ml302Ops = {
    .startUp = ML302_StartUp,
    .shutDown = ML302_ShutDown,
    .init = ML302_Init,
    .reset = ML302_Reset,
    .pdp = ML302_Pdp,
    .opsSocket = {
		.release = ML302_Release,
        .connect = ML302_Connect,
        .disconnect = ML302_Disconnect,
        .send = ML302_Send,
        .recv = ML302_Recv,
		.sendto = ML302_SendTo,
		.recvfrom = ML302_RecvFrom
    }
};

struct RilDeviceConfig ml302Config = {0};
struct RilDeviceStatus ml302Status = {0};

bool isAutoPdp = false;

#ifdef USING_OS
RilSem semML302AT;
#endif

/* Public define -------------------------------------------------------------*/
/* Public macro --------------------------------------------------------------*/
/* Public function prototypes ------------------------------------------------*/
int ML302_GetIdleSocket(void);

extern uint32_t NEDelay_Ms(int n);
extern struct Socket* Ril_GetSocketById(int id);
/********************************************************************************************* 

	ML302 opertions

  ********************************************************************************************/
/************************************
start up, set startup pin high 2s at least
  ***********************************/
int ML302_StartUp(struct RilDevice *device)
{
	device->adapter->pinCtrl(RIL_PIN_STARTUP, 1);
	NEDelay_Ms(3000);
	
	return 0;
}
/************************************
shut down, set startup pin low 3.5s at least
  ***********************************/
int ML302_ShutDown(struct RilDevice *device)
{
	return 0;
}
/************************************
	ML302 Init
  ***********************************/
int ML302_Init(struct RilDevice *device)
{
	int result = 0;
	const char *initCmds[] = {
		"ATE0",
		"AT+CPIN?",
		"AT+CFUN?",
		"AT+CSQ",
		NULL
	};
	char **cmd = (char **)initCmds;
	
#ifdef USING_OS
	RIL_SEM_INIT(&semML302AT, ml302AT, 1);
#endif
	
	// loop cmd list
	do
	{
		RilDevice_ClearMessages();
		result = RilDevice_ExecuteCommand(device, 3000, "OK", *cmd);
		
		/* get nothing*/
		if(result != 0)
		{	return -2;	}
		
		/* get non OK response */
		if(Nat_ResponseCheck(&(device->adapter->response), "OK") == NULL)
		{	return -1;	}
		
		cmd += 1;
	}while(*cmd != NULL);
	
	return 0;
}
/************************************
	ML302 Reset
  ***********************************/
int ML302_Reset(struct RilDevice *device, bool usePin)
{
	return 0;
}
/************************************
	ML302 Pdp
  ***********************************/
int ML302_Pdp(struct RilDevice *device)
{
	const char *initCmds[] = {
		"AT+IMEI",
		"AT+CGMR",
		"AT+CCLK?",
		NULL
	};
	char **cmd = (char **)initCmds;
	int32_t result = -1;
	
	/* step1, get verctrl info */
	result = RilDevice_ExecuteCommand(device, 2000, "OK", "AT+VERCTRL?");
	
	/* step2, if verctrl info is 0.0, set verctrl to 0.1, and reset */
	if(result != 0)
	{	return -1;	}
	
	
	if(isAutoPdp == false)
	{
		result = RilDevice_ExecuteCommand(device, 1000, "OK", "AT+VERCTRL=0,1");
		if(result != 0)
		{	return -1;	}
		RilDevice_ExecuteCommand(device, 1000, 0, "AT+TRB");
		
		/* wait for device finish reset */
		for(int i=0; i<5; i++)
		{
			if(RilDevice_ExecuteCommand(device, 1000, "OK", "AT") == 0)
			{	break;	}
		}
	}
	

	/* execute remain command */
	do
	{
		result = RilDevice_ExecuteCommand(device, 3000, "OK", *cmd);
		
		/* get nothing*/
		if(result != 0)
		{	return -2;	}
		
		cmd += 1;
	}while(*cmd != NULL);
	
	
	/* get imsi */
	result = RilDevice_ExecuteCommand(device, 1000, 0, "AT+CIMI");
	
	for(int i=0; i<RIL_AT_RESPONSE_COUNT; i++)
	{
		if(strlen(device->adapter->response.buf[i]) == 15)
		{	
			memcpy(ml302Status.imsi, device->adapter->response.buf[i], 15);
			break;
		}
	}
	
	return 0;
}
/************************************************************************************************************
	MN316 socket sal api
  ***********************************************************************************************************/
int ML302_Release(struct RilDevice *device, struct Socket *socket)
{}
/************************************
	ML302 Socket Connect
  ***********************************/
int ML302_Connect(struct RilDevice *device, struct Socket *socket, const struct sockaddr *name, socklen_t namelen)
{
	union SocketAddress *address = (union SocketAddress *)name;
	int result = 0;
	char* responseString = NULL;
	
#ifdef USING_OS
	RIL_SEM_TAKE(&semML302AT, -1);
#endif
	result = RilDevice_ExecuteCommand(device, 2000, "OK", "AT+MIPOPEN=%d,\"%s\",\"%s\",%d", 
																      socket->id,
																	  (socket->type == SOCK_STREAM)? "TCP":"UDP",
																	  address->addr_at.sat_addr,
																	  address->addr_at.sat_port);
	
	if(result != 0)
	{	
		result =  -1;	
		goto __end;
	}
	
	
	if((responseString = Nat_ResponseCheck(&(device->adapter->response), "CONNECT OK")) != NULL
		|| (responseString = Nat_ResponseCheck(&(device->adapter->response), "ALREADY CONNECT")) != NULL)
	{
		result =  0;
	}
	else if((responseString = Nat_ResponseCheck(&(device->adapter->response), "CONNECT FAIL")) != NULL)
	{	result = -1;	}
	
__end:
	
#ifdef USING_OS
	RIL_SEM_RELEASE(&semML302AT);
#endif
	
	return result;
}
/************************************
	ML302 Socket Disconnect
  ***********************************/
int ML302_Disconnect(struct RilDevice *device, struct Socket *socket)
{
	int result = 0;
	
#ifdef USING_OS
	RIL_SEM_TAKE(&semML302AT, -1);
#endif
	
	if(socket->id < 0)
	{	return -1;	}
	
	RilDevice_ExecuteCommand(device, 2000, "CLOSE OK", "AT+MIPCLOSE=%d", socket->id);

	
	if(result != 0)
	{
		socket->id = -1;
		return 0;
	}

#ifdef USING_OS
	RIL_SEM_RELEASE(&semML302AT);
#endif
	
	return -1;
}
/************************************
	ML302 Socket Send
  ***********************************/
int ML302_Send(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags)
{
	int result = 0;
	
#ifdef USING_OS
	RIL_SEM_TAKE(&semML302AT, -1);
#endif
	
	/* send packet */
	result = RilDevice_ExecuteCommand(device, 2000, ">", "AT+MIPSEND=%d, %d", socket->id, size);
	if(result != 0)
	{	
		result = -1;
		goto __end;	
	}
	device->adapter->write(data, size);
	
	/* wait for send status */
	if(RilDevice_ExecuteCommand(device, 5000, "SEND OK", NULL) != 0)
	{	
		result = -2;
		goto __end;	
	}
	
__end:
#ifdef USING_OS
	RIL_SEM_RELEASE(&semML302AT);
#endif
	
	return result;
}
/************************************
	ML302 Socket Receive
  ***********************************/
int ML302_Recv(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags)
{
	int count = 0;
	struct SocketStream *tcpObj = (struct SocketStream *)(socket->obj);
	RilDevice_ExecuteCommand(device, 0, 0, NULL);	
	
	if(tcpObj->seek == 0)
	{	return 0;	}
	
	/* get data from buffer rx */
	if((tcpObj->seek + 1) > size)
	{	
		count = size;
		memcpy(mem, tcpObj->buffer, count);
		memcpy(tcpObj->buffer, tcpObj->buffer + count - 1, tcpObj->seek + 1 - count);
		tcpObj->seek = tcpObj->seek + 1 - count;
	}
	else
	{
		count = tcpObj->seek + 1;
		memcpy(mem, tcpObj->buffer, count);
		tcpObj->seek = 0;
	}
	
	return count;
}
/************************************
	ML302 Socket Sendto
  ***********************************/
int ML302_SendTo(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen)
{
	int result = 0;
	
	if(ML302_Connect(device, socket, to, tolen) != 0)
	{	return -1;	}
	
	/* send packet */
	result = RilDevice_ExecuteCommand(device, 1000, ">", "AT+MIPSEND=%d, %d", socket->id, size);
	if(result != 0)
	{	return -1;	}
	device->adapter->write(data, size);
	
	/* wait for send status */
	RilDevice_ExecuteCommand(device, 0, 0, NULL);
	
	return tolen;
}
/************************************
	ML302 Socket Receive from
  ***********************************/
int ML302_RecvFrom(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags, struct sockaddr *from, socklen_t *fromlen)
{
	int countRecv = 0;
	struct SocketDgRam *udpObj = (struct SocketDgRam *)socket->obj;
	
	RilDevice_ExecuteCommand(device, 0, 0, NULL);

	for(int i=0; i<RIL_SOCKET_UDP_LIST_COUNT; i++)
	{
		if(udpObj->msgList[i].start != NULL)
		{
			countRecv = ((udpObj->msgList[i].size > size)? size: udpObj->msgList[i].size);
			memcpy(mem, udpObj->msgList[i].start, countRecv);
			
			if(from != NULL)
			{
				memcpy(from, udpObj->msgList[i].start, sizeof(struct sockaddr));
				*fromlen =  (socklen_t)sizeof(struct sockaddr_at);
			}
			free(udpObj->msgList[i].start);
			udpObj->msgList[i].start = NULL;
			udpObj->msgList[i].size = 0;
			
			break;
		}
	}
	
	return countRecv;
}

/*********************************************************************************************/

//  urc functions

/*********************************************************************************************/
/************************************
	ML302 Sim status
  ***********************************/
int ML302_Sim(char *data)           // "+CPIN: READY"
{   
    if(strstr((char*)data, "READY") != NULL)
    {   ml302Status.simStatus = 1;    }
    else
    {   ml302Status.simStatus = -1;   }
    return 10;
}
/************************************
	ML302 signal
  ***********************************/
int ML302_Signal(char *data)
{   
    int temp32 = 0;
    sscanf(data, "+CSQ: %d,", &temp32);
    ml302Status.csq = temp32;
    return 10;
}
/************************************
	ML302 pdp info
  ***********************************/
int ML302_PdpInfo(char *data)
{
    int temp32 = 0;
    sscanf(data, "+VERCTRL: 0, %d", &temp32);
    isAutoPdp = (temp32 == 1)? true:false;
    return 11;
}
/************************************
	ML302 IMEI
  ***********************************/
int ML302_Imei(char *data)
{
    sscanf(data, "+IMEI: %s", ml302Status.imei);
    
    return 5 + strlen(ml302Status.imei);
}
/************************************
	ML302 CCLK
  ***********************************/
int ML302_Cclk(char *data)
{
	
    return 16;
}
/************************************
	ML302 tx status
  ***********************************/
int ML302_SocketCon(char *data)
{
	int socketId = 0;
	
	sscanf(data, "%d,CLOSED", &socketId);
	
	return 8;
}

/************************************
	ML302 rx urc
  ***********************************/
int ML302_SocketRx(char *data)
{
	int countRecv = 0, socketId = 0, countRemain = 0;
    char *start;
    struct Socket *socket = NULL;
	struct SocketStream *tcpObj = NULL;
	struct SocketDgRam *udpObj = NULL;
  
    if(strncmp(data + 10, "recv", 4) == 0)
    {
        sscanf(data, "+MIPURC: \"recv\",%d,%d", &socketId, &countRecv);
        start = strchr(data, '\n');
        start = start + 1;
        
        socket = Ril_GetSocketById(socketId);
        
		/* put recv packet into socket rx buffer */
		if(socket != NULL)
        {
			switch(socket->type)
			{
				case SOCK_STREAM:
					tcpObj = (struct SocketStream *)(socket->obj);
					countRemain = RIL_SOCKET_TCP_RX_BUFFER_SIZE - tcpObj->seek - 1;
					if(countRemain > countRecv)
					{	
						memcpy(tcpObj->buffer + tcpObj->seek, start, countRecv);
						tcpObj->seek += countRecv;
					}
					else
					{
						memcpy(tcpObj->buffer + tcpObj->seek, start, countRemain);
						tcpObj->seek = RIL_SOCKET_TCP_RX_BUFFER_SIZE - 1;
					}
					break;
				case SOCK_DGRAM:
					udpObj = (struct SocketDgRam *)(socket->obj);
					for(int i=0; i<RIL_SOCKET_UDP_LIST_COUNT; i++)
					{
						if(udpObj->msgList[i].start == NULL
							|| udpObj->msgList[i].size == 0)
						{
							
							udpObj->msgList[i].start = malloc(countRecv);
							if(udpObj->msgList[i].start == NULL)
							{	break;	}
							
							memcpy(udpObj->msgList[i].start, start, countRecv);
							udpObj->msgList[i].size = countRecv;
							
							break;
						}
					}
			}
        }
        return (uint32_t)start - (uint32_t)data + countRecv;
    }
    
    return 8;
}
/************************************
	ML302 gnss info
  ***********************************/
int ML302_GnssInfo(char *data)
{
    char utc[16] = {0}, latitude[16] = {0}, ns[2] = {0}, longitude[16] = {0}, ew[2] = {0};
    sprintf(data + 3, "GGA,%s,%s,%s,%s,%s,", utc, latitude, ns, longitude, ew);
    return 10;
}
/************************************
	ML302 gnss type
  ***********************************/
int ML302_GnssType(char *data)
{

    return 10;
}

RIL_MODULE_INSTALL(ml302)
